#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <sys/types.h>
#include <time.h>

#define ON_LIN

#ifdef ON_LIN
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <netdb.h>

  #define printo(socket, ...) {char buf[8*1024]; int len=sprintf(buf,__VA_ARGS__); send(socket, buf, len, 0);}

  #define TSOCKET int

  int initApp()
  {
      return 1;
  }
#endif

#ifdef ON_WIN
  #include <winsock2.h>

//  #define printo(socket, ...) {printf(__VA_ARGS__);}
  #define printo(socket, ...) {char buf[8*1024]; int len=sprintf(buf,__VA_ARGS__); send(socket, buf, len, 0);}

  #define TSOCKET SOCKET

  int initApp()
  {
    WORD wVersionRequested;
    WSADATA wsaData;
    int wsaerr;
    wVersionRequested = MAKEWORD(2, 2);
    wsaerr = WSAStartup(wVersionRequested, &wsaData);
    if (wsaerr != 0)
    {
//      printf("The Winsock dll not found!\n");
      return 0;
    }
    else
    {
//      printf("The Winsock dll found!\n");
//      printf("The status: %s.\n", wsaData.szSystemStatus);
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 )
    {
//      printf("The dll do not support the Winsock version %u.%u!\n", LOBYTE(wsaData.wVersion),HIBYTE(wsaData.wVersion));
      WSACleanup();
      return 0;
    }
    else
    {
//      printf("The dll supports the Winsock version %u.%u!\n", LOBYTE(wsaData.wVersion),HIBYTE(wsaData.wVersion));
//      printf("The highest version this dll can support: %u.%u\n", LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));
    }

    return 1;
  }
#endif


TSOCKET openSocket(int port, char *ip)
{
  TSOCKET sock = socket( AF_INET, SOCK_STREAM, 0 );
//  printf("Created Socket!\n");

  struct sockaddr_in sain;
  sain.sin_family = AF_INET;
  sain.sin_port = htons(port);
  sain.sin_addr.s_addr = inet_addr(ip);

  if(connect( sock,(struct sockaddr *)&sain, sizeof(sain) )==-1) {
//    printf("connection error\n");
    return 0;
  } else {
//    printf("connected!!\n");
  }

  return sock;
}

void printHttpEnv(TSOCKET socket, char *env)
{
  char *value = strstr(env,"=");

  *value = 0;
  printo(socket,"%s: %s\r\n",env,(value+1));
  *value = '=';
}

#define STARTS_WITH(STR, FIND) ( strstr(STR,FIND) == STR )

void printRequisition(TSOCKET socket)
{
  char *method = getenv("HTTP_METHOD");
  if(!method) {
    method = getenv("REQUEST_METHOD");
  }
  if(!method) {
    method = "GET";
  }

//  printo(socket,"%s %s %s\r\n", method, getenv("REQUEST_URI"), getenv("SERVER_PROTOCOL"));

  char *pathInfo = getenv("PATH_INFO");
  if(!pathInfo) pathInfo="/";
  printo(socket,"%s %s", method, pathInfo);

  char *queryString = getenv("QUERY_STRING");
  if(queryString && strlen(queryString)) {
    printo(socket,"?%s",getenv("QUERY_STRING"));
  }
  printo(socket," %s\r\n",getenv("SERVER_PROTOCOL"));
}

void printHeaders(TSOCKET socket, char *env[])
{
  int i;

  for(i=0; env[i]; i++) {
    if(STARTS_WITH(env[i],"HTTP_METHOD")) {
      continue;
    }

    if(STARTS_WITH(env[i],"HTTP_")) {
      printHttpEnv(socket,env[i]+5);
    }
    if(STARTS_WITH(env[i],"CONTENT_TYPE")) {
      printHttpEnv(socket,env[i]);
    }
    if(STARTS_WITH(env[i],"CONTENT_LENGTH")) {
      printHttpEnv(socket,env[i]);
    }
  }

  printo(socket,"\r\n");
}

void printBody(TSOCKET socket)
{
  #define BUF_LEN 8*1024
  char buf[BUF_LEN];

  while(!feof(stdin)) {
    size_t rdlen = fread(buf, 1, BUF_LEN, stdin);
    send(socket, buf, rdlen, 0);
  }

}

void redirectRawResponse(TSOCKET socket)
{
  #define BUF_LEN 8*1024
  char buf[BUF_LEN];

  while(1) {
    ssize_t rcvlen = recv(socket, buf, BUF_LEN, 0);
    if(rcvlen<1) break;
    fwrite(buf, 1, rcvlen, stdout);
  }
}

void printResponseHeader(char *headers, char *header)
{
  char *headerBegin = strstr(headers,header);

  if(headerBegin) {
    char *headerEnd = strstr(headerBegin,"\r\n");
    char bkp = headerEnd[2];
    headerEnd[2] = 0;
    printf(headerBegin);
    headerEnd[2] = bkp;
  }
}

void redirectHttpResponse(TSOCKET socket)
{
  #define BUF_LEN 8*1024
  char buf[BUF_LEN];

  //read response header
  int sz=0;
  while(1) {
    char b;
    ssize_t rcvlen = recv(socket, &b, 1, 0);
    if(rcvlen<1) return;
    buf[sz] = tolower(b);
    if( (buf[sz]=='\n') && (strstr(buf,"\r\n\r\n")) ) {
      break;
    }
    ++sz;
  }

  //print important response headers
  printResponseHeader(buf, "content-type");
  printResponseHeader(buf, "content-length");
  printf("\r\n");

  //get response length
  unsigned long responseLenRem = 0;
  char *contentLengthStr = strstr(buf,"content-length:");
  if(contentLengthStr) {
    contentLengthStr += 15;
    if(contentLengthStr[0]==' ') contentLengthStr++;
    responseLenRem = atoi(contentLengthStr);
  }

  //print response body
  while(1) {
    ssize_t rcvlen = recv(socket, buf, BUF_LEN, 0);
    if(rcvlen<1) break;
    fwrite(buf, 1, rcvlen, stdout);

    if(rcvlen>=responseLenRem) break;
    responseLenRem -= rcvlen;
  }
}

int main(int argc, char *argv[], char *env[])
{
  initApp();

  TSOCKET socket = openSocket(8081,"127.0.0.1");

//  printf("redirecting http -> socket... \r\n");
  printRequisition(socket);
  printHeaders(socket,env);
  printBody(socket);

//  printf("redirecting socket -> http... \r\n");
//  printf("content-type: text\r\n\r\n");
//  redirectRawResponse(socket);
  redirectHttpResponse(socket);

//  printf("DONE!\r\n");

  return 0;
}


/*
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <time.h>

//#define printo(socket, ...) {printf(__VA_ARGS__);}
#define printo(socket, ...) {char buf[8*1024]; int len=sprintf(buf,__VA_ARGS__); send(socket, buf, len, 0);}

void printHttpEnv(int socket, char *env)
{
  char *value = strstr(env,"=");

  *value = 0;
  printo(socket,"%s: %s\r\n",env,(value+1));
  *value = '=';
}

#define STARTS_WITH(STR, FIND) ( strstr(STR,FIND) == STR )

void printRequisition(int socket)
{
  char *method = getenv("HTTP_METHOD");
  if(!method) {
    method = getenv("REQUEST_METHOD");
  }
  if(!method) {
    method = "GET";
  }

//  printo(socket,"%s %s %s\r\n", method, getenv("REQUEST_URI"), getenv("SERVER_PROTOCOL"));

  char *pathInfo = getenv("PATH_INFO");
  if(!pathInfo) pathInfo="/";
  printo(socket,"%s %s", method, pathInfo);

  char *queryString = getenv("QUERY_STRING");
  if(queryString && strlen(queryString)) {
    printo(socket,"?%s",getenv("QUERY_STRING"));
  }
  printo(socket," %s\r\n",getenv("SERVER_PROTOCOL"));
}

void printHeaders(int socket, char *env[])
{
  int i;

  for(i=0; env[i]; i++) {
    if(STARTS_WITH(env[i],"HTTP_METHOD")) {
      continue;
    }

    if(STARTS_WITH(env[i],"HTTP_")) {
      printHttpEnv(socket,env[i]+5);
    }
    if(STARTS_WITH(env[i],"CONTENT_TYPE")) {
      printHttpEnv(socket,env[i]);
    }
    if(STARTS_WITH(env[i],"CONTENT_LENGTH")) {
      printHttpEnv(socket,env[i]);
    }
  }

  printo(socket,"\r\n");
}

void printBody(int socket)
{
  #define BUF_LEN 8*1024
  char buf[BUF_LEN];

  while(!feof(stdin)) {
    size_t rdlen = fread(buf, 1, BUF_LEN, stdin);
    send(socket, buf, rdlen, 0);
  }

}

void redirectRawResponse(int socket)
{
  #define BUF_LEN 8*1024
  char buf[BUF_LEN];

  while(1) {
    ssize_t rcvlen = recv(socket, buf, BUF_LEN, 0);
    if(rcvlen<1) break;
    fwrite(buf, 1, rcvlen, stdout);
  }
}

void printResponseHeader(char *headers, char *header)
{
  char *headerBegin = strstr(headers,header);

  if(headerBegin) {
    char *headerEnd = strstr(headerBegin,"\r\n");
    char bkp = headerEnd[2];
    headerEnd[2] = 0;
    printf(headerBegin);
    headerEnd[2] = bkp;
  }
}

void redirectHttpResponse(int socket)
{
  #define BUF_LEN 8*1024
  char buf[BUF_LEN];

  //read response header
  int sz=0;
  while(1) {
    char b;
    ssize_t rcvlen = recv(socket, &b, 1, 0);
    if(rcvlen<1) return;
    buf[sz] = tolower(b);
    if( (buf[sz]=='\n') && (strstr(buf,"\r\n\r\n")) ) {
      break;
    }
    ++sz;
  }

  //print important response headers
  printResponseHeader(buf, "content-type");
  printResponseHeader(buf, "content-length");
  printf("\r\n");

  //get response length
  unsigned long responseLenRem = 0;
  char *contentLengthStr = strstr(buf,"content-length:");
  if(contentLengthStr) {
    contentLengthStr += 15;
    if(contentLengthStr[0]==' ') contentLengthStr++;
    responseLenRem = atoi(contentLengthStr);
  }
 
  //print response body
  while(1) {
    ssize_t rcvlen = recv(socket, buf, BUF_LEN, 0);
    if(rcvlen<1) break;
    fwrite(buf, 1, rcvlen, stdout);

    if(rcvlen>=responseLenRem) break;
    responseLenRem -= rcvlen;
  }
}

int openSocket(int port, char *ip)
{
    int sock = socket( AF_INET, SOCK_STREAM, 0 );
//    printf("Created Socket!\n");
        
    // Tell the socket to connect to the IP address we found, on port 80 (HTTP)
    struct sockaddr_in sain;
    sain.sin_family = AF_INET;
    sain.sin_port = htons(port); 
    sain.sin_addr.s_addr = inet_addr(ip);
    
    if(connect( sock,(struct sockaddr *)&sain, sizeof(sain) )==-1) {
//        printf("connection error\n");
        return 0;
    } else {
//        printf("connected!!\n");
    }

    return sock;
}

int main(int argc, char *argv[], char *env[])
{
  int socket = openSocket(8081,"127.0.0.1");

//printf("redirecting http -> socket... \r\n");
  printRequisition(socket);
  printHeaders(socket,env);
  printBody(socket);

//printf("redirecting socket -> http... \r\n");
//  printf("content-type: text\r\n\r\n");
//  redirectRawResponse(socket);
  redirectHttpResponse(socket);

//printf("DONE!\r\n");

  return 0;
}
*/

